package com.appshelves.model;

import java.util.List;

import android.content.pm.ResolveInfo;

import com.appshelves.model.saveable.ChangeableList;
import com.appshelves.utils.ComponentHelper;

class ModelAccessor extends ModelAPI {

    private static final int TOP_SHELF = 0;

    private static ModelAccessor _instance;

    private ChangeableList<Shelf> shelves = new ChangeableList<Shelf>(Shelf.class);
    private ChangeableList<AppData> appData = new ChangeableList<AppData>(AppData.class);

    /// basic access

    @Override
    ChangeableList<Shelf> getShelves() {
        return shelves;
    }

    private void addShelf(Shelf shelf) {
        getShelves().addNew(shelf);
    }

    private void addShelf(int index, Shelf shelf) {
        getShelves().addNew(index, shelf);
    }

    Shelf getShelf(int index) {
        return getShelves().get(index);
    }

    void removeShelf(Shelf shelf) {
        getShelves().remove(shelf);
    }

    @Override
    ChangeableList<AppData> getAppData() {
        return appData;
    }

    private AppData getAppData(int dataID) {
        return getAppData().get(dataID);
    }

    /// Shelves

    private void addShelfToTop(Shelf shelf) {
        addShelf(TOP_SHELF, shelf);
    }

    private void addShelfToBottom(Shelf shelf) {
        addShelf(getOffShelfID(), shelf);
    }

    Shelf addNewShelfToTop() {
        Shelf shelf = new Shelf(getShelves());
        addShelfToTop(shelf);
        return shelf;
    }

    Shelf addNewShelfToBottom() {
        Shelf shelf = new Shelf(getShelves());
        addShelfToBottom(shelf);
        return shelf;
    }

    boolean isShelfEmpty(Shelf shelf) {
        return shelf.isEmpty();
    }

    int getRealShelvesNumber() {
        return getShelves().size() - 1; // excluding off shelf.
    }

    void moveShelfToTop(Shelf shelf) {
        removeShelf(shelf);
        addShelfToTop(shelf);
    }

    /// App - shelf
    List<App> getShelfApps(Shelf shelf) { // in case I change the access way.
        return shelf.getApps();
    }

    int getShelfAppsNumber(Shelf shelf) {
        return getShelfApps(shelf).size();
    }

    Shelf getAppShelf(App app) { // in case I change the accesss way. 
        return app.getShelf();
    }

    private void setAppShelf(Shelf shelf, App app) {
        app.setShelf(shelf);
    }

    private void performAddAppToShelf(Shelf shelf, App app) {
        shelf.add(app);
        setAppShelf(shelf, app);
    }

    private void performAddAppToShelfToSortedPos(Shelf shelf, App app) {
        shelf.addToSortedPos(app);
        setAppShelf(shelf, app);
    }

    private void performAddAppsToShelf(Shelf shelf, List<App> apps) {
        for (App app : apps)
            setAppShelf(shelf, app);
        shelf.add(apps);
    }

    private void performRemoveAppFromShelf(Shelf shelf, App app) {
        shelf.remove(app);
    }

    private void performRemoveAppsFromShelf(Shelf shelf, List<App> apps) {
        shelf.remove(apps);
    }

    void addAppToShelf(Shelf shelf, App app) {
        moveAppToAnotherShelf(app, shelf);
    }

    void addAppsToShelf(Shelf shelf, List<App> apps) {
        removeOffShelfApps(apps);
        performAddAppsToShelf(shelf, apps);
    }

    void removeAppsFromShelf(Shelf shelf, List<App> apps) {
        performRemoveAppsFromShelf(shelf, apps);
        addOffShelfApps(apps);
    }

    void removeAllAppsFromShelf(Shelf shelf) {
        addOffShelfApps(getShelfApps(shelf)); // not calling perfom cauz shelf is going to be deleted.
    }

    void moveAppToAnotherShelf(App app, Shelf shelf) {
        performRemoveAppFromShelf(getAppShelf(app), app);
        performAddAppToShelf(shelf, app);
    }

    int getShelfAppPosition(Shelf shelf, App app) {
        return shelf.getAppPosition(app);
    }

    /// Off shelf

    void createOffShelf(int numApps) {
        addShelf(new Shelf(getShelves(), numApps));
    }

    void allocateOffShelfSpace(int size) {
        getOffShelf().allocateSpace(size);
    }

    private int getOffShelfID() {
        return getShelves().size() - 1;
    }

    Shelf getOffShelf() {
        return getShelf(getOffShelfID());
    }

    List<App> getOffShelfApps() {
        return getShelfApps(getOffShelf());
    }

    int getOffShelfAppsNumber() {
        return getShelfAppsNumber(getOffShelf());
    }

    boolean isOffShelfApp(App app) {
        return getAppShelf(app) == getOffShelf();
    }

    void addOffShelfApp(App app) {
        performAddAppToShelf(getOffShelf(), app);
    }

    private void addOffShelfAppToSortedPos(App app) {
        performAddAppToShelfToSortedPos(getOffShelf(), app);
    }

    private void addOffShelfApps(List<App> apps) {
        performAddAppsToShelf(getOffShelf(), apps);
        sortOffShelf();
    }

    private void removeOffShelfApps(List<App> apps) {
        performRemoveAppsFromShelf(getOffShelf(), apps);
    }

    int getOffShelfAppPosition(App app) {
        return getShelfAppPosition(getOffShelf(), app);
    }

    void sortOffShelf() {
        getOffShelf().sortAlphabetically();
    }

    ///

    App findAppByPackageName(String packageName) {
        for (Shelf shelf : getShelves())
            for (App app : getShelfApps(shelf))
                if (app.getComponentNameString().startsWith(packageName))
                    return app;
        return null;
    }

    void addNewApp(ResolveInfo info) {
        addOffShelfApp(new App(info));
    }

    App addNewAppToSortedPos(ResolveInfo info) {
        App app = new App(info);
        addOffShelfAppToSortedPos(app);
        return app;
    }

    void deleteApp(Shelf shelf, App app) {
        shelf.deleteApp(app);
    }

    public App updateApp(String packageName) {
        App app = findAppByPackageName(packageName);
        if (app == null)
            return null;
        app.updateByInfo(ComponentHelper.resolveInfoByPackageName(packageName));
        return app;
    }

    /// App - AppData

    private App getAppByData(AppData data) {
        return data.getApp();
    }

    void addAppDataAndBind(App app, ResolveInfo info) {
        AppData data = new AppData(getAppData(), info);
        getAppData().addNew(data);
        bindAppData(app, data, lastAddedDataID());
    }

    void bindAppDataOnLoad(App app, int dataID) {
        AppData data = getAppData(dataID);
        bindAppData(app, data);
    }

    void deleteAppData(int dataID) {

        for (int i = dataID + 1; i < getAppData().size(); i++) {
            getAppByData(getAppData(i)).bindData(i - 1);
        }

        getAppData().remove(dataID);
    }

    private void bindAppData(App app, AppData data) {
        app.bindData(data);
        data.bindApp(app);
    }

    private void bindAppData(App app, AppData data, int dataID) {
        app.bindData(data, dataID);
        data.bindApp(app);
    }

    private int lastAddedDataID() {
        return getAppData().size() - 1;
    }

    ///

    static ModelAccessor instance() {
        if (_instance == null)
            _instance = new ModelAccessor();
        return _instance;
    }

}
